package com.edouardbataille.open.handler
{
	import __AS3__.vec.Vector;

	import com.edouardbataille.open.MySqlSocket;
	import com.edouardbataille.open.MysqlConst;
	import com.edouardbataille.open.bytes.ByteReader;
	import com.edouardbataille.open.result.Result;
	import com.edouardbataille.open.result.ResultSet;
	import com.edouardbataille.open.result.SqlMetadata;

	import flash.utils.ByteArray;

	import mx.rpc.Fault;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;

	public class StatementResultHandler extends AbstractHandler implements IHandler
	{
		public function StatementResultHandler ()
		{
		}

		private function readHeader (data : ByteReader) : void
		{
			var length : uint = data.readPacketLength();
			var packet : uint = data.readPacketNum();
			fieldCount = data.readByte() & 0xFF;
			if (fieldCount != 0xFF)
			{
				data.position -= 1;
				fieldCount = data.readLengthCode();
				lastResult = new ResultSet(fieldCount);
			}
		}

		public function handleData (packets : Vector.<ByteReader>, con : MySqlSocket) : void
		{
			var data : ByteReader;

			if (!header)
			{
				header = true;
				data = packets.shift ();
				readHeader(data);

				if (fieldCount == 0xFF)
				{
					var fault : Fault = FaultParser.parseFault(data);
					dispatchEvent(FaultEvent.createEvent(fault));
					return;
				}

				if (fieldCount == 0)
				{
					var affectedRow : uint = data.readByte();
					var insert_id : uint = data.readLengthCode();
					var serverStatus : uint = data.readUShort();
					var warning : uint = data.readUShort();
					lastResult.lastInsertId = insert_id;
					dispatchEvent(ResultEvent.createEvent(lastResult));
					return;
				}
			}

			for (; fieldCountRead < fieldCount; ++fieldCountRead)
			{
				data = packets.shift();
				var length : uint = data.readPacketLength();
				var packet : uint = data.readPacketNum();
				readField(data, fieldCountRead);
			}

			if (!firstEOFReaded)
			{
				data = packets.shift();
				if (!readEOF(data))
					return; //TODO change by throw Error ();
				else
					firstEOFReaded = true;
			}

			while (!secondEOFReaded && packets.length > 0)
			{
				data = packets.shift();
				if (readEOF(data))
					secondEOFReaded = true;
				else
				{
					var rowLength : uint = data.readPacketLength();
					var rowPacket : uint = data.readPacketNum();
					var row : ByteReader = new ByteReader();
					readRow(data);
				}
			}
			if (secondEOFReaded)
				dispatchEvent(ResultEvent.createEvent(lastResult));
		}

		private function readEOF (data : ByteReader) : Boolean
		{
			var reading : uint = 4;
			var length : uint = data.readPacketLength();
			var packet : uint = data.readPacketNum();
			if (length == 5)
			{
				reading = 5;
				var value : uint = data.readByte() & 0xFF;
				if (value == 0xFE)
				{
					data.position += 4;
					return true;
				}
			}
			data.position -= reading;
			return false;
		}

		private function readField (data : ByteReader, pos : uint) : void
		{
			var catalog : String = data.readLenString();
			var db : String = data.readLenString();
			var table : String = data.readLenString();
			var org_table : String = data.readLenString();
			var name : String = data.readLenString();
			var org_name : String = data.readLenString();
			var filler : uint = data.readByte(); // 1
			var charSet : uint = data.readUShort(); // 2
			var lengthField : int = data.readMultipleByte(4); // 4
			var type : uint = data.readByte() & 0xFF; // 1
			var flag : uint = data.readUShort(); // 2
			var decimal : int = data.readByte() & 0xFF; // 1
			filler = data.readShort(); // 2;
			var defaultValue : uint = data.readMultipleByte(data.bytesAvailable);

			var metaData : SqlMetadata = new SqlMetadata(catalog, db, table, org_table, name, org_name, lengthField, type, flag, decimal);
			lastResult.addMeta(metaData, pos);
		}

		private function readRow (data : ByteReader) : void
		{
			while (data.bytesAvailable)
			{
				var nullBitmap : Array = new Array();
				++data.position;
				nullBitmap.push(data.readByte() & 0xFF);
				var nbToRead : uint = Math.floor((fieldCount + 1) / 8);
				for (var i : int = 0; i < nbToRead; ++i)
				{
					nullBitmap.push(data.readByte() & 0xFF);
				}
				var row : Object = new Object();
				var arr : Array = new Array();
				var length : uint;
				var date : Date;
				var offSet : int = 1;
				var nullBitmapOffset : uint = 0;
				for each (var meta : SqlMetadata in lastResult.metaDatas)
				{
					if (row.hasOwnProperty(meta.column))
						trace("WARNING you have two vars wich have same name (" + meta.column + ") don't use Object 'result'");
					if (offSet == 7)
					{
						++nullBitmapOffset;
						offSet = -1;
					}
					++offSet;
					var nullValue : uint = ((nullBitmap[nullBitmapOffset] >> offSet) & 0x1);
					if (nullValue == 1)
					{
						row[meta.column] = null;
						arr.push(null);
						continue;
					}
					if (data.bytesAvailable == 0)
						break;
					switch (meta.type)
					{
						case MysqlConst.FIELD_TYPE_NULL:
							break;
						case MysqlConst.FIELD_TYPE_DECIMAL:
						case MysqlConst.FIELD_TYPE_NEWDECIMAL:
							var decimalStr : String = data.readLenString();
							var decimal : Number = parseFloat(decimalStr);
							row[meta.column] = decimal;
							arr.push(decimal);
							break;
						case MysqlConst.FIELD_TYPE_YEAR:
						case MysqlConst.FIELD_TYPE_SHORT:
							var short : int = data.readMultipleByte(2);
							row[meta.column] = short;
							arr.push(short);
							break;
						case MysqlConst.FIELD_TYPE_INT24:
						case MysqlConst.FIELD_TYPE_LONG:
							var long : int = data.readMultipleByte(4);
							row[meta.column] = long;
							arr.push(long);
							break;
						case MysqlConst.FIELD_TYPE_LONGLONG:
							var longlong : int = data.readMultipleByte(8);
							row[meta.column] = longlong;
							arr.push(longlong);
							break;
						case MysqlConst.FIELD_TYPE_DOUBLE:
							var double : Number = data.readMultipleByte(8);
							row[meta.column] = double;
							arr.push(double);
							break;
						case MysqlConst.FIELD_TYPE_FLOAT:
							var float : Number = data.readMultipleByte(4);
							row[meta.column] = float;
							arr.push(float);
							break;
						case MysqlConst.FIELD_TYPE_ENUM:
						case MysqlConst.FIELD_TYPE_STRING:
						case MysqlConst.FIELD_TYPE_VAR_STRING:
						case MysqlConst.FIELD_TYPE_VARCHAR:
							var str : String = data.readLenString();
							row[meta.column] = str;
							arr.push(str);
							break;
						case MysqlConst.FIELD_TYPE_TINY:
							var tiny : int = data.readByte();
							row[meta.column] = tiny;
							arr.push(tiny);
							break;
						case MysqlConst.FIELD_TYPE_NEWDATE:
						case MysqlConst.FIELD_TYPE_DATE:
						case MysqlConst.FIELD_TYPE_DATETIME:
							date = new Date();
							length = data.readByte() & 0xFF;
							length = readDate(data, date, length);
							readTime(data, date, length);
							row[meta.column] = date;
							arr.push(date);
							break;
						case MysqlConst.FIELD_TYPE_TIME:
							length = data.readByte() & 0xFF;
							var sign : uint = 0;
							var day : uint = 0;
							if (length > 0)
								sign = data.readByte() & 0xFF;
							if (length > 0)
								day = data.readMultipleByte(4);
							if (length > 0)
								length -= 5;
							date = new Date();
							readTime(data, date, length);
							row[meta.column] = date;
							arr.push(date);
							break;
						case MysqlConst.FIELD_TYPE_GEOMETRY:
						case MysqlConst.FIELD_TYPE_TINY_BLOB:
						case MysqlConst.FIELD_TYPE_MEDIUM_BLOB:
						case MysqlConst.FIELD_TYPE_LONG_BLOB:
						case MysqlConst.FIELD_TYPE_BLOB:
							var bLength : uint = data.readLengthCode();
							var bytes : ByteArray = new ByteArray();
							if (bLength > 0)
								data.readBytes(bytes, 0, bLength);
							row[meta.column] = bytes;
							arr.push(bytes);
							break;
					}
				}
				var res : Result = new Result();
				res.result = row;
				res.array = arr;
				lastResult.addRow(res);
			}
		}

		private function readDate (data : ByteReader, date : Date, length : uint) : uint
		{
			if (length == 0)
			{
				date.setFullYear(0, 0, 0);
				date.setHours(0, 0, 0);
				return 0;
			}
			var year : uint = data.readMultipleByte(2);
			var month : uint = 0
			var day : uint = 0;
			length -= 2;
			if (length > 0)
			{
				month = data.readByte() & 0xFF;
				month -= 1;
				--length;
			}
			if (length > 0)
			{
				day = data.readByte() & 0xFF;
				--length;
			}
			date.setFullYear(year);
			date.setMonth(month);
			date.setDate(day);
			date.setHours(0, 0, 0, 0);
			return length;
		}

		private function readTime (data : ByteReader, date : Date, length : uint) : uint
		{
			var hours : uint = 0;
			var minutes : uint = 0;
			var seconds : uint = 0;
			var milli : uint = 0;
			if (length > 0)
			{
				hours = data.readByte() & 0xFF;
				--length;
			}

			if (length > 0)
			{
				minutes = data.readByte() & 0xFF;
				--length;
			}

			if (length > 0)
			{
				seconds = data.readByte() & 0xFF;
				--length;
			}
			if (length > 0)
			{
				milli = data.readMultipleByte(length);
				length = 0;
			}
			date.setHours(hours);
			date.setMinutes(minutes);
			date.setSeconds(seconds);
			date.setMilliseconds(milli);
			return length;
		}

	}
}